package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.mq.OrderTo;
import com.atguigu.common.mq.StockDetailTo;
import com.atguigu.common.mq.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.ao.WareSkuLockAo;
import com.atguigu.gulimall.ware.entity.SkuHasStockVo;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {



    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private WareOrderTaskService taskService;

    @Autowired
    private WareOrderTaskDetailService taskDetailService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private OrderFeignService orderFeignService;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIdS) {
        List<SkuHasStockVo> vos=skuIdS.stream().map(e->{
            SkuHasStockVo vo=new SkuHasStockVo();
           Integer count= baseMapper.getSkuStock(e);
            vo.setSkuId(e);
            vo.setHasStock(count != 0 && count > 0);
            return vo;
        }).collect(Collectors.toList());
        return vos;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean lockStock(WareSkuLockAo ao) {
        log.info("开始锁定库存，ao={}", JSON.toJSONString(ao));

         //* 保存库存工作单详情信息
         //* 追溯
        WareOrderTaskEntity wareOrderTaskEntity=new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(ao.getOrderSn());
        wareOrderTaskEntity.setCreateTime(new Date());
        taskService.save(wareOrderTaskEntity);


        //查询商品库存
        List<OrderItemVo> locks = ao.getLocks();
        List<SkuWareHasStock> collect = locks.stream().map(e -> {
            SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
            List<Long> wareIds = baseMapper.listWareIdHasSkuStock(e.getSkuId());
            skuWareHasStock.setWareId(wareIds);
            skuWareHasStock.setSkuId(e.getSkuId());
            skuWareHasStock.setNum(e.getCount());
            return skuWareHasStock;
        }).collect(Collectors.toList());
        log.info("lockStock 查询到的，库存信息 coolect={}",JSON.toJSONString(collect));
        for (SkuWareHasStock stock:collect) {
            Boolean skuStocked=false;
            List<Long> wareIds = stock.getWareId();
            if (CollectionUtils.isEmpty(wareIds)){
                //没有任何仓库有这个商品的库存
                throw new NoStockException(stock.getSkuId());
            }
            for (Long wareId:wareIds) {
                Long count = wareSkuDao.lockSkuStock(stock.getSkuId(),wareId,stock.getNum());
                if(count==1){
                    skuStocked=true;
                    WareOrderTaskDetailEntity taskDetailEntity= WareOrderTaskDetailEntity.builder()
                            .skuId(stock.getSkuId())
                            .skuName("")
                            .skuNum(stock.getNum())
                            .taskId(wareOrderTaskEntity.getId())
                            .wareId(wareId)
                            .lockStatus(1)
                            .build();
                    taskDetailService.save(taskDetailEntity);

                    //发送mq消息
                    StockLockedTo stockLockedTo=new StockLockedTo();
                    stockLockedTo.setId(wareOrderTaskEntity.getId());

                    StockDetailTo stockDetailTo=new StockDetailTo();
                    BeanUtils.copyProperties(taskDetailEntity,stockDetailTo);
                    stockLockedTo.setDetailTo(stockDetailTo);

                    //告诉MQ库存锁定成功，生产者-交换机-死信队列-交换机-普通队列-消费者
                    //消息内容为库存锁定单传输对象，里面包括库存单id和库存详情单对象
                    log.info("发送锁定库存消息");
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);
                    break;
                }else {
                    //重新试下一个厂库
                }
            }
            if (!skuStocked){
                //没有任何仓库有这个商品的库存
                throw new NoStockException(stock.getSkuId());
            }
        }
        return true;
    }



    /**
     * 1、查询数据库关于这个订单的锁库存消息
     *    有，证明库存锁定成功了。
     *      1、没有这个订单。必须解锁
     *      2、有这个订单。不是解锁库存。
     *          订单状态：已取消：解锁库存
     *          订单状态：没取消：不能解锁
     *    没有，库存锁定失败了，库存回滚了。这种情况无需解锁
     */
    @Override
    public void unlockStock(StockLockedTo to) {
        log.info("开始解锁库存，ao={}", JSON.toJSONString(to));
        StockDetailTo detailTo = to.getDetailTo();
        WareOrderTaskDetailEntity taskDetail = taskDetailService.getById(detailTo.getId());

        if (taskDetail!=null && taskDetail.getLockStatus().equals(1)){
            Long taskId = to.getId();
            WareOrderTaskEntity wareOrderTaskEntity = taskService.getById(taskId);
            R r = orderFeignService.getOrderStatus(wareOrderTaskEntity.getOrderSn());

            if (r.getCode().equals(0)){
                //获取订单数据
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });
                if (data ==null || data.getStatus().equals(4)){
                    unlock(detailTo.getSkuId(),detailTo.getWareId(),detailTo.getSkuNum(),detailTo.getId());
                }else {
                    log.info("订单异常");
                }

            } else if (r.getCode().equals(BizCodeEnum.NO_ORDER.getCode())){
                unlock(detailTo.getSkuId(),detailTo.getWareId(),detailTo.getSkuNum(),detailTo.getId());
            } else {
                // 消息拒绝以后重新放到队列里面，让别人继续消费解锁
                throw new RuntimeException("远程服务失败");
            }
        }else {
            log.info("库存已解锁");
        }
    }

    @Transactional
    @Override
    public void unlockStockOrderHandle(OrderTo to) {
        String orderSn = to.getOrderSn();

        WareOrderTaskEntity order_sn = taskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
        Long taskId=order_sn.getId();

        List<WareOrderTaskDetailEntity> list = taskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", taskId)
                .eq("lock_status", 1));
        for (WareOrderTaskDetailEntity entity:list) {
            unlock(entity.getSkuId(),entity.getWareId(),entity.getSkuNum(),entity.getId());
        }

    }


    private void unlock(Long skuId,Long wareId,Integer num,Long taskDetailId){

        wareSkuDao.unlock(skuId,wareId,num);

        WareOrderTaskDetailEntity ware =new WareOrderTaskDetailEntity();

        ware.setId(taskDetailId);
        ware.setLockStatus(2);//解锁
        taskDetailService.updateById(ware);
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareId;
    }

}